
`define rot_up_1(in)     {in[2:0],in[3]}
`define rot_up(in, n)    {in[3-n:0],in[3:4-n]}

module keccakf_round(
    input [99:0] in,
    input [2:0] round_const_1,
    output [99:0] out
);
    wire [3:0] a[24:0],b[24:0],c[24:0],d[24:0],e[24:0];
    wire [3:0] g[4:0];
    
    genvar x,y;
    
    //**********  a[x][y][z]=in[w(5y+x)+z]  **********
    generate
        for(y=0;y<5;y=y+1)
            begin:L1
                for(x=0; x<5; x=x+1)
                    begin:L2
                        assign a[5*x+y]=in[99-4*(5*y+x):96-4*(5*y+x)];
                    end
        end
    endgenerate
    
    //round 1
    
    //**********  b=theta(a)  **********
    //g[x]=a[x][0]^ a[x][1]^ ... ^a[x][4]
    assign g[0]=a[0]^a[1]^a[2]^a[3]^a[4];
    assign g[1]=a[5]^a[6]^a[7]^a[8]^a[9];
    assign g[2]=a[10]^a[11]^a[12]^a[13]^a[14];
    assign g[3]=a[15]^a[16]^a[17]^a[18]^a[19];
    assign g[4]=a[20]^a[21]^a[22]^a[23]^a[24];
    //b=theta(a)
    assign b[0]=a[0]^g[4]^`rot_up_1(g[1]);
    assign b[5]=a[5]^g[0]^`rot_up_1(g[2]);
    assign b[10]=a[10]^g[1]^`rot_up_1(g[3]);
    assign b[15]=a[15]^g[2]^`rot_up_1(g[4]);
    assign b[20]=a[20]^g[3]^`rot_up_1(g[0]);
    assign b[1]=a[1]^g[4]^`rot_up_1(g[1]);
    assign b[6]=a[6]^g[0]^`rot_up_1(g[2]);
    assign b[11]=a[11]^g[1]^`rot_up_1(g[3]);
    assign b[16]=a[16]^g[2]^`rot_up_1(g[4]);
    assign b[21]=a[21]^g[3]^`rot_up_1(g[0]);
    assign b[2]=a[2]^g[4]^`rot_up_1(g[1]);
    assign b[7]=a[7]^g[0]^`rot_up_1(g[2]);
    assign b[12]=a[12]^g[1]^`rot_up_1(g[3]);
    assign b[17]=a[17]^g[2]^`rot_up_1(g[4]);
    assign b[22]=a[22]^g[3]^`rot_up_1(g[0]);
    assign b[3]=a[3]^g[4]^`rot_up_1(g[1]);
    assign b[8]=a[8]^g[0]^`rot_up_1(g[2]);
    assign b[13]=a[13]^g[1]^`rot_up_1(g[3]);
    assign b[18]=a[18]^g[2]^`rot_up_1(g[4]);
    assign b[23]=a[23]^g[3]^`rot_up_1(g[0]);
    assign b[4]=a[4]^g[4]^`rot_up_1(g[1]);
    assign b[9]=a[9]^g[0]^`rot_up_1(g[2]);
    assign b[14]=a[14]^g[1]^`rot_up_1(g[3]);
    assign b[19]=a[19]^g[2]^`rot_up_1(g[4]);
    assign b[24]=a[24]^g[3]^`rot_up_1(g[0]);

    //**********  c=rho(b)  **********
    //c=rho(b)
    assign c[0]=b[0];
    assign c[5]=`rot_up_1(b[5]);
    assign c[2]=`rot_up(b[2],3);
    assign c[10]=`rot_up(b[10],2);
    assign c[15]=b[15];
    assign c[20]=`rot_up(b[20],3);
    assign c[1]=b[1];
    assign c[6]=b[6];
    assign c[11]=`rot_up(b[11],2);
    assign c[16]=`rot_up(b[16],3);
    assign c[21]=b[21];
    assign c[7]=`rot_up(b[7],2);
    assign c[12]=`rot_up(b[12],3);
    assign c[17]=`rot_up_1(b[17]);
    assign c[22]=`rot_up(b[22],3);
    assign c[3]=`rot_up_1(b[3]);
    assign c[8]=`rot_up_1(b[8]);
    assign c[13]=`rot_up(b[13],3);
    assign c[18]=`rot_up_1(b[18]);
    assign c[23]=b[23];
    assign c[4]=`rot_up(b[4],2);
    assign c[9]=`rot_up(b[9],2);
    assign c[14]=`rot_up_1(b[14]);
    assign c[19]=b[19];
    assign c[24]=`rot_up(b[24],2);
    //**********  d=pi(c)  **********
    //d=pi(c)
    assign d[0]=c[0];
    assign d[1]=c[15];
    assign d[2]=c[5];
    assign d[3]=c[20];
    assign d[4]=c[10];
    assign d[5]=c[6];
    assign d[6]=c[21];
    assign d[7]=c[11];
    assign d[8]=c[1];
    assign d[9]=c[16];
    assign d[10]=c[12];
    assign d[11]=c[2];
    assign d[12]=c[17];
    assign d[13]=c[7];
    assign d[14]=c[22];
    assign d[15]=c[18];
    assign d[16]=c[8];
    assign d[17]=c[23];
    assign d[18]=c[13];
    assign d[19]=c[3];
    assign d[20]=c[24];
    assign d[21]=c[14];
    assign d[22]=c[4];
    assign d[23]=c[19];
    assign d[24]=c[9];
    
    //**********  e=iota(chi(d))  **********
    //e=iota(chi(d))
    assign e[0]=((d[0]^((~d[5])&d[10])))^{round_const_1[2],1'b0,round_const_1[1],round_const_1[0]};
    assign e[5]=d[5]^((~d[10])&d[15]);
    assign e[10]=d[10]^((~d[15])&d[20]);
    assign e[15]=d[15]^((~d[20])&d[0]);
    assign e[20]=d[20]^((~d[0])&d[5]);
    assign e[1]=d[1]^((~d[6])&d[11]);
    assign e[6]=d[6]^((~d[11])&d[16]);
    assign e[11]=d[11]^((~d[16])&d[21]);
    assign e[16]=d[16]^((~d[21])&d[1]);
    assign e[21]=d[21]^((~d[1])&d[6]);
    assign e[2]=d[2]^((~d[7])&d[12]);
    assign e[7]=d[7]^((~d[12])&d[17]);
    assign e[12]=d[12]^((~d[17])&d[22]);
    assign e[17]=d[17]^((~d[22])&d[2]);
    assign e[22]=d[22]^((~d[2])&d[7]);
    assign e[3]=d[3]^((~d[8])&d[13]);
    assign e[8]=d[8]^((~d[13])&d[18]);
    assign e[13]=d[13]^((~d[18])&d[23]);
    assign e[18]=d[18]^((~d[23])&d[3]);
    assign e[23]=d[23]^((~d[3])&d[8]);
    assign e[4]=d[4]^((~d[9])&d[14]);
    assign e[9]=d[9]^((~d[14])&d[19]);
    assign e[14]=d[14]^((~d[19])&d[24]);
    assign e[19]=d[19]^((~d[24])&d[4]);
    assign e[24]=d[24]^((~d[4])&d[9]);

    //**********  out_temp[w(5y+x)+z] == ee[x][y][z]  **********
    generate
        for(y=0;y<5;y=y+1)
            begin:L3
                for(x=0; x<5; x=x+1)
                    begin:L4
                        assign out[99-4*(5*y+x):96-4*(5*y+x)]=e[5*x+y];
                    end
        end
    endgenerate
    
endmodule

`undef rot_up
`undef rot_up_1
